#include "pixel_lib.h"

#include "dwarf_utils.h"
#include "elog.h"
#include "elog_file.h"

using namespace std;

unsigned int objectIDArray[MAX_OBJ_ITEMS];

void InitPixelArray(pixel *image, unsigned int size) {
	unsigned int i;
	
	for(i=0;i<size;i++) {
		image[i].x=0xFFFFFFFF;
		image[i].y=0xFFFFFFFF;
		image[i].mag=0xFFFFFFFF;
		image[i].objectID = NULL;
		
	}
	for(i=0; i<MAX_OBJ_ITEMS; i++) {
		objectIDArray[i] = i;
	}
}

void InitObjectArray(imgObject *object, unsigned int size) {
	unsigned int i;
	for(i=0;i<size;i++) {
		object[i].x=0;
		object[i].y=0;
		object[i].width=0;
		object[i].height=0;
		object[i].mass=0;
		object[i].numberItems=0;
		object[i].objectID = 0;
		object[i].score = 0;
	}
}

unsigned int *SearchPixels(pixel *image, unsigned int size, unsigned int x, unsigned int y, unsigned int width) {
	/*unsigned int i;
	for(i=0;i<size;i++) {
		if(image[i].objectID == NULL) {
			return -1;
		}
		if(image[i].x == x && image[i].y == y) {
			return i;
		}
	}
	*/
	if(image[y*width+x].objectID == NULL) {
		return NULL;
	}
	else {
		return image[y*width+x].objectID;
	}
}

unsigned int ClassifyPixels(pixel *image, unsigned int size,  Mat imageSource, unsigned int *pixelsFound) {
	int i,j;
	unsigned int height;
	unsigned int width;
	unsigned int widthStep;
	// unsigned short *data;
	unsigned char *data;
	unsigned int *checkObjID;
	unsigned int pixelCount = 0;
	unsigned int pixelIndex = 0;
	unsigned int objectArrayIndex = 0;

	height = imageSource.rows;
	width = imageSource.cols;
	// widthStep = imageSource.step / sizeof(unsigned short);
	// data = (unsigned short*)imageSource.data;
	widthStep = imageSource.step;
	data = imageSource.data;
	// log_i("height:%d, width:%d, widthStep:%d, data[0]:%d", height, width, widthStep, data[0]);

	for(i=1; i<height; i++) {
		for(j=1;j<width; j++) {
			// log_i("data[%d]%d", i*widthStep+j, data[i*widthStep+j]);
			if(data[i*widthStep+j] != 0) {
				if(pixelCount == 65535 || objectArrayIndex == 65535) {
					if(pixelCount == 65535)
						log_i("too many pixels!");
					if(objectArrayIndex == 65535)
						log_i("too many ohject!");
					return 0;
				}

				//Create a new pixel instance
				image[i*width+j].x = j;
				image[i*width+j].y = i;
				image[i*width+j].mag = data[i*widthStep+j];

				//Search for neighbors that have an objectID
				//Check for a pixel above
				checkObjID = SearchPixels(image, size, j, i-1,width);
				if(checkObjID != NULL) {
					// log_i("UP ObjectID = %d", *checkObjID);
					image[i*width+j].objectID = checkObjID;
					//See if there was a pixel to the left of it, and if so, change its objectID to the one above
					checkObjID = SearchPixels(image,size,j-1,i,width);
					if(checkObjID != NULL) {
						*image[i*width+(j-1)].objectID = *image[i*width+j].objectID;
					}
				}
				else {
					//no top pix found, check the left pixel
					checkObjID = SearchPixels(image, size, j-1, i,width);
					if(checkObjID != NULL) {	
						// log_i("LEFT ObjectID = %d", *checkObjID);
						image[i*width+j].objectID = checkObjID;
					}
					else {
						//No nearby objects found, create new objectID
						image[i*width+j].objectID = &objectIDArray[objectArrayIndex];
						// log_i("NEW ObjectID = %d", *image[i*width+j].objectID);
						objectArrayIndex++;	
					}
				}
				pixelCount++;
			}
			// log_i("data = 0");
		}
	}
	// log_i("check over");
/*
	for(i=0; i<objectArrayIndex; i++) {
		log_i("ObjectIDArray[%d] = %d", i, objectIDArray[i]);
	}
*/

	if(objectArrayIndex == 0 || objectArrayIndex == 1) {
		objectArrayIndex = 1;
		log_i("too few pixel!");
	}
	
	*pixelsFound = pixelCount;
	return objectIDArray[objectArrayIndex-1];
}                                                             

void	CleanUpObjects(pixel *image, pixel *imageReduced, unsigned int size, unsigned int pixelCount) {
	unsigned int i;
	unsigned int pixelIndex=0;

	for(i=0; i<size && pixelIndex < pixelCount; i++) {
		if(image[i].objectID == NULL) {}
		else {
			imageReduced[pixelIndex++] = image[i];
		}
	}	
	/*		
	for(i=0; i<pixelCount; i++) {
		log_i("X: %d, Y: %d, mag: %d, objectID %d",image[i].x,image[i].y,image[i].mag, *image[i].objectID);
	}
	*/
}

int ContourClassifyObjects(imgObject** imageStar, unsigned int *starCountMain, const Mat& imgGray) {
	if(imgGray.empty())
		return -10;

	unsigned int considerObjectsCount = 0;
	unsigned int starCount = 0;
	double areaMax = 0;

	vector<vector<Point> > contours;
	findContours(imgGray, contours, noArray(), RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	// log_i("objects found:%d", (unsigned int)contours.size());
	imgObject *imageObject = (imgObject*) calloc(contours.size(), sizeof(imgObject));

	for (int k = 0; k < contours.size(); k++) {
		RotatedRect minRect = minAreaRect(contours[k]);

		double area_1 = contourArea(contours[k]);
		areaMax = area_1 > areaMax ? area_1 : areaMax;
		if (area_1 < 5 || area_1 > 1000)  
			continue;
		considerObjectsCount++;

		// float widthOnHeight = minRect.size.width / minRect.size.height;
		// if(widthOnHeight >= 1.2 || widthOnHeight <= 0.8)
		// 	continue;

		Rect bRect = boundingRect(contours[k]);
		if(abs(bRect.width - bRect.height) >= 5)
			continue;

		float tempX = 0;
		float tempY = 0;
		unsigned int tempMass = 0;
		unsigned int noneZeroPix = 0;

		for(int i = bRect.y; i < bRect.y + bRect.height; i++) {
			for(int j = bRect.x; j < bRect.x + bRect.width; j++) {
				tempX += (float) j * (float) imgGray.at<uchar>(i, j);
				tempY += (float) i * (float) imgGray.at<uchar>(i, j);
				tempMass += (unsigned int)imgGray.at<uchar>(i, j);
				if(imgGray.at<uchar>(i, j))
					noneZeroPix++;
			}
		}
		imageObject[starCount].numberItems = noneZeroPix;
		imageObject[starCount].x = tempX/(float)tempMass; 
		imageObject[starCount].y = tempY/(float)tempMass;
		imageObject[starCount].width = bRect.width;
		imageObject[starCount].height = bRect.height;
		imageObject[starCount].mass = tempMass;
		imageObject[starCount].objectID = starCount;
		imageObject[starCount].img_width = imgGray.cols;
		imageObject[starCount].img_height = imgGray.rows;
		starCount++;
	}
	// log_i("Max contour area:%f", areaMax);
	if(considerObjectsCount != 0) {
		// float SNR = (float)considerObjectsCount / (float)totalObjects;
		// log_i("SNR:%f", SNR);
		// if(log10(SNR) < -3.0) {
		// 	log_i("cloud found!");
		// 	xfree(imageObject);
		// 	return -1;
		// }
		// if(areaMax > 3000.0) {
		// 	log_i("cloud found!");
		// 	xfree(imageObject);
		// 	return -1;
		// }
		if(considerObjectsCount < 10) {
			log_i("star not enough");
			xfree(imageObject);
			return -2;
		}
		float starRatio = (float)starCount / (float)considerObjectsCount;
		// log_i("starCount:%d, considerObjectsCount:%d, starRatio:%f", starCount, considerObjectsCount, starRatio);
		if(starRatio < 0.3) {
			log_i("star line detected!");
			xfree(imageObject);
			return -3;
		}
	}
	else {
		log_i("No star found!");
		xfree(imageObject);
		return -4;
	}
	*starCountMain = starCount;
	*imageStar = imageObject;
	return 0;
}

unsigned int ClassifyObjects(imgObject* imageObject, unsigned int objectSize, pixel* image, unsigned int pixelSize, Mat img){
	unsigned int i,j;
	unsigned int objectsFound=0;
	unsigned int objectCounter=0;
	unsigned int minX =0xFFFFFFFF;
	unsigned int minY =0xFFFFFFFF;
	unsigned int maxX =0;
	unsigned int maxY =0;
	float tempX =0;
	float tempY =0;
	unsigned int tempMass =0;
	
	for(i=0; i<=objectSize; i++) {
		for(j=0; j<pixelSize; j++) {
			if(*image[j].objectID == i) {
				tempX += (float) image[j].x * (float) image[j].mag;
				tempY += (float) image[j].y * (float) image[j].mag;
				tempMass += image[j].mag;

				objectsFound++;
				//Used to calculate the width and height
				if(image[j].x < minX) minX = image[j].x;
				if(image[j].x > maxX) maxX = image[j].x;
				if(image[j].y < minY) minY = image[j].y;
				if(image[j].y > maxY) maxY = image[j].y;
				//We only want stars, not nebula stuff
				if(maxX-minX+1 > 100)
					break;
				if(maxY-minY+1 > 100)
					break;
			}
		}
		if(objectsFound > 5) {
			if(maxX-minX+1 < 100) {
				if(maxY-minY+1 < 100) {	
					//stars should be round, make sure we only use stars	
					if(abs(int(maxX-minX+1) - int(maxY-minY+1)) < 5) {
						imageObject[objectCounter].numberItems = objectsFound;
						imageObject[objectCounter].x = tempX/(float)tempMass; 
						imageObject[objectCounter].y = tempY/(float)tempMass;
						imageObject[objectCounter].width = maxX - minX +1;
						imageObject[objectCounter].height = maxY - minY +1;
						imageObject[objectCounter].mass = tempMass;
						imageObject[objectCounter].objectID = objectCounter;
						imageObject[objectCounter].img_width = img.cols;
						imageObject[objectCounter].img_height = img.rows;
						objectCounter++;
					}
				}
			}
		}
		
		minX =0xFFFFFFFF;
		minY =0xFFFFFFFF;
		maxX =0;
		maxY =0;
		tempX =0;
		tempY =0;
		tempMass =0;
		objectsFound = 0;
	}



	return objectCounter;
}

void RankObjects(imgObject *imageObjects, unsigned int size) {
	int i,j;
	float temp;

	for(i=0;i<size;i++) {
		for(j=0; j<size; j++) {
			if(j<=i) {
		
			}
			else {
				temp = 	imageObjects[i].mass*imageObjects[j].mass/sqrt(pow((signed)imageObjects[i].x-(signed)imageObjects[j].x,2)+ pow((signed)imageObjects[i].y-(signed)imageObjects[j].y,2));
				imageObjects[i].score += temp;
				imageObjects[j].score += temp; 
			}		
		}
	}	
	
	for(i=0;i<size;i++) {
		for(j=0; j<size-1; j++) {
			if(imageObjects[j].score < imageObjects[j+1].score) {
				SwapObjects(&imageObjects[j], &imageObjects[j+1]);
			}
		}
	}
/*	
	for(i=0; i<size; i++) {
		log_i("|| ObjID: %d || x: %f || y: %f || width: %d || height: %d || mass: %d || score: %f \n ", imageObjects[i].objectID, imageObjects[i].x, imageObjects[i].y, imageObjects[i].width, imageObjects[i].height,	imageObjects[i].mass, imageObjects[i].score);
	}
*/
}

void SwapObjects(imgObject *obj1, imgObject *obj2) {
	imgObject objTemp;

	objTemp.numberItems = obj2->numberItems;
	objTemp.x = obj2->x;
	objTemp.y = obj2->y;
	objTemp.width = obj2->width;
	objTemp.height = obj2->height;
	objTemp.mass = obj2->mass;
	objTemp.objectID = obj2->objectID;
	objTemp.score = obj2->score;
	
	obj2->numberItems = obj1->numberItems;
	obj2->x = obj1->x;
	obj2->y = obj1->y;
	obj2->width = obj1->width;
	obj2->height = obj1->height;
	obj2->mass = obj1->mass;
	obj2->objectID = obj1->objectID;
	obj2->score = obj1->score;

	obj1->numberItems = objTemp.numberItems;
	obj1->x = objTemp.x;
	obj1->y = objTemp.y;
	obj1->width = objTemp.width;
	obj1->height = objTemp.height;
	obj1->mass = objTemp.mass;
	obj1->objectID = objTemp.objectID;
	obj1->score = objTemp.score;
}

